// 使用递归来实现一个深度克隆，可以复制一个目标对象，返回一个完整拷贝
// 被复制的对象类型会被限制为数字、字符串、布尔、日期、数组、Object对象。不会包含函数、正则对象等

//method 1 可实现深拷贝，但被拷贝对象中不能有undefined ， function， RegExp 等等类型
/*---------------------------------------------------------*/
function deepClone1(obj) {
  return JSON.parse(JSON.stringify(obj));
}

// method 2 当检测目标为 基本数据类型、function、Date、RegExp 时，直接返回（首次判断及递归时的拦截）
/*---------------------------------------------------------*/
function deepClone2(target) {
  //两种判断方式：

  /* if (
    Object.prototype.toString.call(target) !== "[object Object]" &&
    Object.prototype.toString.call(target) !== "[object Array]"
  ) {
    return target;
  } */

  if (
    typeof target !== "object" ||
    target === null ||
    target instanceof Date ||
    target instanceof RegExp
  )
    return target;

  // 判断目标时数组还是对象
  const newTarget = Array.isArray(target) ? [] : {};

  // 遍历对象（数组则遍历下标）将引用类型递归遍历，基本类型直接赋值到新对象（数组）的对应的键上
  Object.keys(target).forEach(
    (key) =>
      (newTarget[key] =
        target[key] instanceof Object ? deepClone(target[key]) : target[key])
  );

  // 返回深拷贝后的对象
  return newTarget;
}

//method 3 使用属性描述的获取与设置方法，可以一行代码深拷贝，不仅可以拷贝嵌套对象和函数等，还会连带拷贝属性的特性
//缺点：优点也是缺点，原对象中 不可迭代 或 symbol类型 的属性也会被拷贝过来，而且原对象属性configurable为false的话拷过来也挺麻烦
/*---------------------------------------------------------*/
function deepClone3(obj) {
  return Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
}

// 测试用例：
var srcObj = {
  a: 1,
  b: {
    b1: ["hello", "hi"],
    b2: "JavaScript",
  },
};
var abObj = srcObj;
var tarObj = deepClone3(srcObj);

srcObj.a = 2;
srcObj.b.b1[0] = "Hello";

console.log(abObj.a);
console.log(abObj.b.b1[0]);

console.log(tarObj.a); // 1
console.log(tarObj.b.b1[0]); // "hello"
